package com.kymatrix.mpo.plus.bpm.server.controller.task;

import cn.hutool.core.collection.CollUtil;
import com.kymatrix.mpo.plus.system.api.service.UserApi;
import com.kymatrix.mpo.plus.system.api.dto.UserDto;
import com.kymatrix.mpo.plus.system.api.service.DeptApi;
import com.kymatrix.mpo.plus.system.api.dto.DeptmentDto;
import com.kymatrix.mpo.plus.bpm.server.convert.task.BpmProcessInstanceConvert;
import com.kymatrix.mpo.plus.bpm.server.flowable.util.BpmnModelUtils;
import com.kymatrix.mpo.plus.bpm.server.model.entity.definition.BpmCategoryEntity;
import com.kymatrix.mpo.plus.bpm.server.model.entity.definition.BpmProcessDefinitionInfoEntity;
import com.kymatrix.mpo.plus.bpm.server.model.form.instance.CancelBpmProcessInstanceReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.instance.SaveBpmProcessInstanceReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.instance.QueryBpmProcessInstanceResp;
import com.kymatrix.mpo.plus.bpm.server.model.form.instance.PageQueryBpmProcessInstanceReq;
import com.kymatrix.mpo.plus.bpm.server.service.BpmCategoryService;
import com.kymatrix.mpo.plus.bpm.server.service.BpmProcessDefinitionService;
import com.kymatrix.mpo.plus.bpm.server.service.BpmProcessInstanceService;
import com.kymatrix.mpo.plus.bpm.server.service.BpmTaskService;
import com.kymatrix.mpo.plus.common.business.common.PageResp;
import com.kymatrix.mpo.plus.common.business.common.ResultResp;
import com.kymatrix.mpo.plus.common.springsecurity.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertList;
import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertSet;

/**
 * 流程实例，通过流程定义创建的一次“申请”
 */
@Api(tags = "BPM 流程实例相关请求处理类")
@RestController
@Validated
public class BpmProcessInstanceController {

    @Resource
    private BpmProcessInstanceService processInstanceService;
    @Resource
    private BpmTaskService taskService;
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private BpmCategoryService categoryService;

    @Resource
    private UserApi userApi;
    @Resource
    private DeptApi deptApi;

    @GetMapping("/bpm/process-instance/my-page")
    @ApiOperation(value = "获得我的实例分页列表", notes = "在【我的流程】菜单中，进行调用")
    public ResultResp<PageResp<QueryBpmProcessInstanceResp>> getProcessInstanceMyPage(
            @Valid PageQueryBpmProcessInstanceReq req) {
        PageResp<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(
                SecurityUtils.getLoginUserId(), req);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return ResultResp.ok(new PageResp<>());
        }

        // 拼接返回
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(
                convertList(pageResult.getList(), HistoricProcessInstance::getId));
        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(
                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
        Map<String, BpmCategoryEntity> categoryMap = categoryService.getCategoryMap(
                convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
        return ResultResp.ok(BpmProcessInstanceConvert.buildProcessInstancePage(pageResult,
                processDefinitionMap, categoryMap, taskMap, null, null));
    }

    @GetMapping("/bpm/process-instance/manager-page")
    @ApiOperation(value = "获得管理流程实例的分页列表", notes = "在【流程实例】菜单中，进行调用")
    public ResultResp<PageResp<QueryBpmProcessInstanceResp>> getProcessInstanceManagerPage(
            @Valid PageQueryBpmProcessInstanceReq req) {
        PageResp<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(null, req);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return ResultResp.ok(new PageResp<>());
        }

        // 拼接返回
        Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(
                convertList(pageResult.getList(), HistoricProcessInstance::getId));
        Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(
                convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
        Map<String, BpmCategoryEntity> categoryMap = categoryService.getCategoryMap(
                convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
        // 发起人信息
        Map<String, UserDto> userMap = userApi.getUserMap(
                convertSet(pageResult.getList(), processInstance -> processInstance.getStartUserId()));
        Map<String, DeptmentDto> deptMap = deptApi.getDeptMap(
                convertSet(userMap.values(), UserDto::getDeptId));
        return ResultResp.ok(BpmProcessInstanceConvert.buildProcessInstancePage(pageResult,
                processDefinitionMap, categoryMap, taskMap, userMap, deptMap));
    }

    @PostMapping("/bpm/process-instance/create")
    @ApiOperation(value = "新建流程实例")
    public ResultResp<String> createProcessInstance(@Valid @RequestBody SaveBpmProcessInstanceReq req) {
        return ResultResp.ok(processInstanceService.createProcessInstance(SecurityUtils.getLoginUserId(), req));
    }

    @GetMapping("/bpm/process-instance/get")
    @ApiOperation(value = "获得指定流程实例", notes = "在【流程详细】界面中，进行调用")
    public ResultResp<QueryBpmProcessInstanceResp> getProcessInstance(@RequestParam("id") String id) {
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id);
        if (processInstance == null) {
            return ResultResp.ok(null);
        }

        // 拼接返回
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
                processInstance.getProcessDefinitionId());
        BpmProcessDefinitionInfoEntity processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
                processInstance.getProcessDefinitionId());
        String bpmnXml = BpmnModelUtils.getBpmnXml(
                processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
        UserDto startUser = userApi.getUser(processInstance.getStartUserId());
        DeptmentDto dept = null;
        if (startUser != null) {
            dept = deptApi.getDept(startUser.getDeptId());
        }
        return ResultResp.ok(BpmProcessInstanceConvert.buildProcessInstance(processInstance,
                processDefinition, processDefinitionInfo, bpmnXml, startUser, dept));
    }

    @DeleteMapping("/bpm/process-instance/cancel-by-start-user")
    @ApiOperation(value = "用户取消流程实例", notes = "取消发起的流程")
    public ResultResp<Boolean> cancelProcessInstanceByStartUser(
            @Valid @RequestBody CancelBpmProcessInstanceReq req) {
        processInstanceService.cancelProcessInstanceByStartUser(SecurityUtils.getLoginUserId(), req);
        return ResultResp.ok(true);
    }

    @DeleteMapping("/bpm/process-instance/cancel-by-admin")
    @ApiOperation(value = "管理员取消流程实例", notes = "管理员撤回流程")
    public ResultResp<Boolean> cancelProcessInstanceByManager(
            @Valid @RequestBody CancelBpmProcessInstanceReq req) {
        processInstanceService.cancelProcessInstanceByAdmin(SecurityUtils.getLoginUserId(), req);
        return ResultResp.ok(true);
    }

}
